package com.pactera.miyuangroup.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.pactera.miyuangroup.common.BufferedServletRequestWrapper;
import com.pactera.miyuangroup.common.Constants;
import com.pactera.miyuangroup.common.RedisOperation;
import com.pactera.miyuangroup.util.JwtUtils;
import com.pactera.miyuangroup.vo.request.BaseRequest;
import com.pactera.miyuangroup.vo.response.ResultModel;
import com.pactera.miyuangroup.vo.response.ResultStatusCode;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Base64;

/**
 * jwt
 *
 * @author Wangwu
 * @create 2018-03-30 下午2:28
 **/
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

    private RedisOperation redis;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, RedisOperation redis) {
        super(authenticationManager);
        this.redis = redis;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException {
        String header = request.getHeader("Authorization");
        try {
            //验证  Authorization 是否有token 如果没有，查看参数是否提交
            if (StringUtils.isBlank(header)) {
                //备份HttpServletRequest
                //request = new BufferedServletRequestWrapper(request);
                //使用流
                //InputStream is = request.getInputStream();
                //BaseRequest req = JSON.parseObject(is, BaseRequest.class, Feature.AutoCloseSource);
                header = request.getParameter("token");
            }
            //获取redis token
            String token = redis.get(Constants.REDIS_AUTHORIZATION_TOKEN + header, String.class, "");
            if (StringUtils.isBlank(token)) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json; charset=utf-8");
                response.getWriter().write(JSON.toJSONString(ResultModel.fail(ResultStatusCode.INVALID_TOKEN)));
                response.getWriter().close();
                return;
            }
            //还原jwt token
            token = new String(Base64.getDecoder().decode(token), "utf-8");
            if (token == null || !token.startsWith(JwtUtils.getAuthorizationHeaderPrefix())) {
                chain.doFilter(request, response);
                return;
            }
            UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(token);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            chain.doFilter(request, response);
        } catch (Exception e) {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().write(JSON.toJSONString(ResultModel.fail(ResultStatusCode.INVALID_TOKEN)));
            response.getWriter().close();
        }

    }

    private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String token) {
        token = token.replace(JwtUtils.getAuthorizationHeaderPrefix(), "");
        String user = Jwts.parser()
                .setSigningKey(JwtUtils.getAuthorizationPrivateSecret())
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
        if (null != user) {
            return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        }
        return null;
    }
}
